/******************************************************************************************
版权所有 2021-2021, 长春理工大学C&I实验室。保留所有权利。
文件名：mpp40_vi.c
作者：Wind    版本：V1.0    日期：2021.1.8
文件描述：
    对Hi3516EV200视频编码过程调用MPP操作的封装。
其它说明：

历史修改记录：
1. 2021-1-8:V1.0 Wind
创建。

******************************************************************************************/
//+------------------------------------------------------------------------------------------+
//|                                          文件说明
//+------------------------------------------------------------------------------------------+
//|      VI（Video Input，视频输入）模块可以对接收到的原始视频图像数据进行处理，实现视频数据
//|  的采集。
//|      为便于实验室的学习和修改，本文件中将添加额外的注释信息。
//+------------------------------------------------------------------------------------------+
//|  编写参考《HiMPP V4.0媒体处理软件开发参考(2019-3-5)》（简称《参考》）。    
//+------------------------------------------------------------------------------------------+
//|                                         头文件包含
//+------------------------------------------------------------------------------------------+
/*|*/#include <stdio.h>       //提供printf等基本输入输出
/*|*/#include <pthread.h>     //提供线程操作
/*|*/#include <sys/ioctl.h>   //提供IO口控制
/*|*/#include <sys/prctl.h>   //用于设置ISP线程名
/*|*/#include <fcntl.h>       //提供对设备文件的open操作
/*|*/#include <unistd.h>      //使用Unix系统服务，例如close
/*|*/#include "hi_type.h"     //该文件中定义了海思的数据类型
/*|*/#include "hi_comm_vi.h"  //该文件中定义了VI的数据类型
/*|*/#include "hi_common.h"   //该文件中定义了海思的数据类型
/*|*/#include "hi_mipi.h"     //该文件中定义了对MIPI设备操作定义
/*|*/#include "hi_ae_comm.h"  //该文件提供了海思AE库声明
/*|*/#include "hi_awb_comm.h" //该文件提供了海思AWB库声明
/*|*/#include "hi_sns_ctrl.h" //该文件提供了对sensor传感器控制对象
/*|*/#include "mpi_sys.h"     //该文件提供了MPI-SYS函数声明
/*|*/#include "mpi_isp.h"     //该文件提供了MPI-ISP函数声明
/*|*/#include "mpi_vi.h"      //该文件提供了MPI-VI函数声明
/*|*/#include "mpi_ae.h"      //该文件提供了MPI-AE函数声明
/*|*/#include "mpi_awb.h"     //该文件提供了MPI-AWB函数声明
//|
/*|*/#include "mpp40_debug.h"
/*|*/#include "mpp40_vi.h"
//+------------------------------------------------------------------------------------------+
//|                                         全局声明
//+------------------------------------------------------------------------------------------+
/*|*/static HI_S32 ISP_Run(ISP_DEV IspDev);
/*|*/static HI_S32 ISP_BindSns(ISP_DEV IspDev, HI_U32 u32SnsId, HI_S8 s8SnsDev);
/*|*/static HI_S32 ISP_Sensor_UnRegiter_callback(ISP_DEV IspDev);
/*|*/static HI_S32 ISP_Sensor_Regiter_callback(ISP_DEV IspDev, HI_U32 u32SnsId);
//+------------------------------------------------------------------------------------------+
//|                                          全局变量
//+------------------------------------------------------------------------------------------+
//|    ----------------------------------------
//|    检查SENSOR宏定义
//|    ----------------------------------------
/*|*/#if (!defined SENSOR_IMX307)&&(!defined SENSOR_GC2053)
/*|*/  #error No defined SENEOR_TYPE
/*|*/#endif
//|
/*|*/#if (defined SENSOR_IMX307)&&(defined SENSOR_GC2053)
/*|*/  #error Redefined SENEOR_TYPE
/*|*/#endif
//|
//|    ----------------------------------------
//|    VI设备的属性结构体
//|    ----------------------------------------
/*|*/#ifdef SENSOR_IMX307
/*|*/  static VI_DEV_ATTR_S DEV_ATTR_IMX307_2M_BASE=
/*|*/  {
/*|*/      VI_MODE_MIPI,
/*|*/      VI_WORK_MODE_1Multiplex,
/*|*/      {0xFFF00000, 0x0},
/*|*/      VI_SCAN_PROGRESSIVE,
/*|*/      {-1, -1, -1, -1},
/*|*/      VI_DATA_SEQ_YUYV,
//|    
/*|*/      {
/*|*/          /* port_vsync   port_vsync_neg  port_hsync  port_hsync_neg */
/*|*/          VI_VSYNC_PULSE, VI_VSYNC_NEG_LOW, VI_HSYNC_VALID_SINGNAL, VI_HSYNC_NEG_HIGH, VI_VSYNC_VALID_SINGAL, VI_VSYNC_VALID_NEG_HIGH,
//|            ----------------------------------------
//|            此成员适用D1连接，在MIPI下无效
//|            ----------------------------------------
/*|*/          {
/*|*/              /* hsync_hfb    hsync_act    hsync_hhb */
/*|*/              0, 1280,0,
/*|*/              /* vsync0_vhb vsync0_act vsync0_hhb */
/*|*/              0, 720,0,
/*|*/              /* vsync1_vhb vsync1_act vsync1_hhb */
/*|*/              0, 0, 0
/*|*/          }
/*|*/      },
/*|*/      VI_DATA_TYPE_RGB,
/*|*/      HI_FALSE,
/*|*/      {1920, 1080},
/*|*/      {
/*|*/          {
/*|*/              {1920 ,1080},
/*|*/  
/*|*/          },
/*|*/          {
/*|*/              VI_REPHASE_MODE_NONE,
/*|*/              VI_REPHASE_MODE_NONE
/*|*/          }
/*|*/      },
/*|*/      {
/*|*/          WDR_MODE_NONE,
/*|*/          1080
/*|*/      },
/*|*/      DATA_RATE_X1
/*|*/  };
/*|*/#endif
/*|*/#ifdef SENSOR_GC2053
/*|*/  static VI_DEV_ATTR_S DEV_ATTR_GC2053_2M_BASE =
/*|*/  {
/*|*/      VI_MODE_MIPI,
/*|*/      VI_WORK_MODE_1Multiplex,
/*|*/      {0xFFC00000,    0x0},
/*|*/      VI_SCAN_PROGRESSIVE,
/*|*/      { -1, -1, -1, -1},
/*|*/      VI_DATA_SEQ_YUYV,
/*|*/  
/*|*/      {
/*|*/          /*port_vsync   port_vsync_neg     port_hsync        port_hsync_neg        */
/*|*/          VI_VSYNC_PULSE, VI_VSYNC_NEG_LOW, VI_HSYNC_VALID_SINGNAL, VI_HSYNC_NEG_HIGH, VI_VSYNC_VALID_SINGAL, VI_VSYNC_VALID_NEG_HIGH,
/*|*/  
/*|*/          /*hsync_hfb    hsync_act    hsync_hhb*/
/*|*/          {
/*|*/              0,            1280,        0,
/*|*/              /*vsync0_vhb vsync0_act vsync0_hhb*/
/*|*/              0,            720,        0,
/*|*/              /*vsync1_vhb vsync1_act vsync1_hhb*/
/*|*/              0,            0,            0
/*|*/          }
/*|*/      },
/*|*/      VI_DATA_TYPE_RGB,
/*|*/      HI_FALSE,
/*|*/      {1920 , 1080},
/*|*/      {
/*|*/          {
/*|*/              {1920 , 1080},
/*|*/          },
/*|*/          {
/*|*/              VI_REPHASE_MODE_NONE,
/*|*/              VI_REPHASE_MODE_NONE
/*|*/          }
/*|*/      },
/*|*/      {
/*|*/          WDR_MODE_NONE,
/*|*/          1080
/*|*/      },
/*|*/      DATA_RATE_X1
/*|*/  };
/*|*/#endif
//|
//|    ----------------------------------------
//|    VI的PIPE属性结构体
//|    ----------------------------------------
/*|*/#ifdef SENSOR_IMX307
/*|*/static VI_PIPE_ATTR_S PIPE_ATTR_1920x1080_RAW12_420_3DNR_RFR=
/*|*/{
/*|*/    VI_PIPE_BYPASS_NONE, HI_FALSE, HI_FALSE,
/*|*/    1920, 1080,
/*|*/    PIXEL_FORMAT_RGB_BAYER_12BPP,
/*|*/    COMPRESS_MODE_NONE,
/*|*/    DATA_BITWIDTH_12,
/*|*/    HI_FALSE,
/*|*/    {
/*|*/        PIXEL_FORMAT_YVU_SEMIPLANAR_420,
/*|*/        DATA_BITWIDTH_8,
/*|*/        VI_NR_REF_FROM_RFR,
/*|*/        COMPRESS_MODE_NONE
/*|*/    },
/*|*/    HI_FALSE,
/*|*/    {-1, -1}
/*|*/};
/*|*/#endif
/*|*/#ifdef SENSOR_GC2053
/*|*/  static VI_PIPE_ATTR_S PIPE_ATTR_1920x1080_RAW10_420_3DNR_RFR =
/*|*/  {
/*|*/      VI_PIPE_BYPASS_NONE, HI_FALSE, HI_FALSE,
/*|*/      1920, 1080,
/*|*/      PIXEL_FORMAT_RGB_BAYER_10BPP,
/*|*/      COMPRESS_MODE_NONE,
/*|*/      DATA_BITWIDTH_10,
/*|*/      HI_TRUE,
/*|*/      {
/*|*/          PIXEL_FORMAT_YVU_SEMIPLANAR_420,
/*|*/          DATA_BITWIDTH_8,
/*|*/          VI_NR_REF_FROM_RFR,
/*|*/          COMPRESS_MODE_NONE
/*|*/      },
/*|*/      HI_FALSE,
/*|*/      { -1, -1}
/*|*/  };
/*|*/#endif
//|
//|    ----------------------------------------
//|    VI的通道属性结构体
//|    ----------------------------------------
/*|*/#if (defined SENSOR_IMX307)||(defined SENSOR_GC2053)
/*|*/  static VI_CHN_ATTR_S CHN_ATTR_1920x1080_420_SDR8_LINEAR=
/*|*/  {
/*|*/      {1920, 1080},
/*|*/      PIXEL_FORMAT_YVU_SEMIPLANAR_420,
/*|*/      DYNAMIC_RANGE_SDR8,
/*|*/      VIDEO_FORMAT_LINEAR,
/*|*/      COMPRESS_MODE_NONE,
/*|*/      0,0,
/*|*/      0,
/*|*/      {-1, -1}
/*|*/  };
/*|*/#endif
//|
//|    ----------------------------------------
//|    ISP公共属性结构体
//|    ----------------------------------------
/*|*/#if (defined SENSOR_IMX307)||(defined SENSOR_GC2053)
/*|*/  static ISP_PUB_ATTR_S ISP_PUB_ATTR_MIPI_2M_30FPS=
/*|*/  {
/*|*/      {0, 0, 1920, 1080},
/*|*/      {1920, 1080},
/*|*/      30,
/*|*/      BAYER_RGGB,
/*|*/      WDR_MODE_NONE,
/*|*/      0,
/*|*/  };
/*|*/#endif
//|
//|    ----------------------------------------
//|              MIPI设备的属性结构体
//|    Hi3516EV200只有两条LANE，分别为lane 0和
//|    lane 2，所以lane _id配置为{0,2,-1,-1}。
//|    ——《MIPI使用指南》P65、P54~P55
//|    ----------------------------------------
/*|*/#ifdef SENSOR_IMX307
/*|*/  static combo_dev_attr_t MIPI_2lane_CHN0_SENSOR_IMX307_12BIT_2M_NOWDR_ATTR =
/*|*/  {
/*|*/      .devno = 0,
/*|*/      .input_mode = INPUT_MODE_MIPI,
/*|*/      .data_rate  = MIPI_DATA_RATE_X1,
/*|*/      .img_rect   = {0, 0, 1920, 1080},
/*|*/      {
/*|*/          .mipi_attr = 
/*|*/          {
/*|*/              DATA_TYPE_RAW_12BIT,
/*|*/              HI_MIPI_WDR_MODE_NONE,
/*|*/              {0, 2, -1, -1}
/*|*/          }
/*|*/      }
/*|*/  };
/*|*/#endif
/*|*/#ifdef SENSOR_GC2053
/*|*/  static combo_dev_attr_t MIPI_2lane_CHN0_SENSOR_GC2053_10BIT_2M_NOWDR_ATTR =
/*|*/  {
/*|*/      .devno = 0,
/*|*/      .input_mode = INPUT_MODE_MIPI,
/*|*/      .data_rate = MIPI_DATA_RATE_X1,
/*|*/      .img_rect = {0, 0, 1920, 1080},
/*|*/      {
/*|*/          .mipi_attr =
/*|*/          {
/*|*/              DATA_TYPE_RAW_10BIT,
/*|*/              HI_MIPI_WDR_MODE_NONE,
/*|*/              {0, 2, -1, -1}
/*|*/          }
/*|*/      }
/*|*/  };
/*|*/#endif
//|
//|    ----------------------------------------
//|                 VI相关参数结构体
//|    当函数传入NULL时，调用此结构体进行缺省配置
//|    ----------------------------------------
/*|*/static VI_PARA_S stVI_para_default=
/*|*/{
/*|*/    .enAUTOExposure=HI_TRUE,
/*|*/    .exposuretime=0x4000,//默认值0x4000
/*|*/};
//|
/*|*/static pthread_t g_IspPid = 0; //ISP线程的句柄
/*|*/static VI_DEV  ViDev      = 0; //VI设备号，hi3516EV200只支持一个设备
/*|*/static VI_PIPE ViPipe     = 0; //VI的物理PIPE，Hi3516EV200最大支持两个
/*|*/static VI_CHN  ViChn      = 0; //VI通道号，hi3516EV200只有一个物理通道
//|
/*|*/VI_PARA_S *pstVI_para = &stVI_para_default;
//+------------------------------------------------------------------------------------------+
//|  函数名称：set_mipi
//|  功能描述：设置MIPI设备
//|  参数说明：无
//|  返回值说明：成功返回0
//|  备注：
//+------------------------------------------------------------------------------------------+
static int set_mipi(void)
{
    HI_S32 s32Ret = HI_SUCCESS;
    HI_S32 fd;
    combo_dev_t devno = 0;
    combo_dev_attr_t stcomboDevAttr;
    sns_clk_source_t SnsDev = 0;
    lane_divide_mode_t lane_divide_mode = LANE_DIVIDE_MODE_0;
    fd = open("/dev/hi_mipi", O_RDWR);
    do
    {
        if(fd < 0)
        {
            MPP_OUTPUT("[MPP40-VI-ERROR]Open hi_mipi dev failed!\n");
            break;
        }
        
        /* 设置MIPI模式 */
        s32Ret = ioctl(fd, HI_MIPI_SET_HS_MODE, &lane_divide_mode);
        if(HI_SUCCESS != s32Ret)
        {
            MPP_OUTPUT("[MPP40-VI-ERROR]HI_MIPI_SET_HS_MODE failed!\n");
            break;
        }
        
        /* 使能MIPI时钟 */
        s32Ret = ioctl(fd, HI_MIPI_ENABLE_MIPI_CLOCK, &devno);
        if(HI_SUCCESS != s32Ret)
        {
            MPP_OUTPUT("[MPP40-VI-ERROR]MIPI_ENABLE_CLOCK %d failed!\n", devno);
            break;
        }
        
        /* 重置MIPI */
        s32Ret = ioctl(fd, HI_MIPI_RESET_MIPI, &devno);
        if(HI_SUCCESS!=s32Ret)
        {
            MPP_OUTPUT("[MPP40-VI-ERROR]RESET_MIPI %d failed!\n",devno);
            break;
        }
        
        /* 使能sensor时钟 */
        s32Ret = ioctl(fd, HI_MIPI_ENABLE_SENSOR_CLOCK, &SnsDev);
        if(HI_SUCCESS != s32Ret)
        {
            MPP_OUTPUT("[MPP40-VI-ERROR]HI_MIPI_ENABLE_SENSOR_CLOCK failed!\n");
            break;
        }
        
        s32Ret = ioctl(fd, HI_MIPI_RESET_SENSOR, &SnsDev);
        if(HI_SUCCESS != s32Ret)
        {
            MPP_OUTPUT("[MPP40-VI-ERROR]HI_MIPI_RESET_SENSOR failed!\n");
            break;
        }
        
        /* 设置MIPI属性 */
        //----------------------------------------
        //[说明]对芯片型号的获取在例程中是用于下一
        //步对MIPI属性设置时，适配不同的初始化结构
        //体。
        //本程序中指定了sensor，故获取芯片型号可以
        //不进行。
        //芯片型号定义在当前版本头文件autoconf.h中。
        //
        //[注意]在设置或更换stcomboDevAttr结构体的
        //时候，需要注意当前芯片支持的MIPIlane通道。
        //例如在Hi3516EV200中仅支持lane0、lane2。
        //详细说明见相应结构体注释。
        //----------------------------------------
        /*
        HI_U32 u32ChipId=0;
        s32Ret=HI_MPI_SYS_GetChipId(&u32ChipId);
        if (s32Ret==0)
        {
            MPP_OUTPUT("[MPP40-VI-INFO]Chip ID:%#x\n",u32ChipId);
        }
        */
#ifdef SENSOR_IMX307
        hi_memcpy(&stcomboDevAttr,
                  sizeof(combo_dev_attr_t),
                  &MIPI_2lane_CHN0_SENSOR_IMX307_12BIT_2M_NOWDR_ATTR,
                  sizeof(combo_dev_attr_t));
#endif
#ifdef SENSOR_GC2053
        hi_memcpy(&stcomboDevAttr,
                  sizeof(combo_dev_attr_t),
                  &MIPI_2lane_CHN0_SENSOR_GC2053_10BIT_2M_NOWDR_ATTR,
                  sizeof(combo_dev_attr_t));
#endif
        stcomboDevAttr.devno = 0;
        s32Ret = ioctl(fd, HI_MIPI_SET_DEV_ATTR, &stcomboDevAttr);
        if(HI_SUCCESS != s32Ret)
        {
            MPP_OUTPUT("[MPP40-VI-ERROR]MIPI_SET_DEV_ATTR failed with %#x!\n", s32Ret);
            break;
        }
        
        /* 反重置MIPI */
        s32Ret = ioctl(fd, HI_MIPI_UNRESET_MIPI, &devno);
        if(HI_SUCCESS != s32Ret)
        {
            MPP_OUTPUT("[MPP40-VI-ERROR]HI_MIPI_UNRESET_MIPI[%d] failed!\n", devno);
            break;
        }
        /* 反重置传感器 */
        s32Ret = ioctl(fd, HI_MIPI_UNRESET_SENSOR, &SnsDev);
        if(HI_SUCCESS != s32Ret)
        {
            MPP_OUTPUT("[MPP40-VI-ERROR]HI_MIPI_UNRESET_SENSOR failed!\n");
            break;
        }
        close(fd);
        return 0;
    }while(0);
    
    close(fd);
    return -1;
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：enable_vi_device
//|  功能描述：使能VI设备
//|  参数说明：无
//|  返回值说明：成功返回0
//|  备注：
//+------------------------------------------------------------------------------------------+
int enable_vi_device(void)
{
    HI_S32 s32Ret = HI_SUCCESS;
    VI_DEV_ATTR_S stViDevAttr;
#ifdef SENSOR_IMX307
    hi_memcpy(&stViDevAttr, sizeof(VI_DEV_ATTR_S), &DEV_ATTR_IMX307_2M_BASE, sizeof(VI_DEV_ATTR_S));
    //stViDevAttr.stWDRAttr.enWDRMode=WDR_MODE_NONE;
#endif
#ifdef SENSOR_GC2053
    hi_memcpy(&stViDevAttr, sizeof(VI_DEV_ATTR_S), &DEV_ATTR_GC2053_2M_BASE, sizeof(VI_DEV_ATTR_S));
#endif

    s32Ret = HI_MPI_VI_SetDevAttr(ViDev, &stViDevAttr);
    if(s32Ret !=HI_SUCCESS)
    {
        MPP_OUTPUT("[MPP40-VI-ERROR]HI_MPI_VI_SetDevAttr failed with %#x!\n", s32Ret);
        vi_uninit();
        return -1;
    }

    s32Ret = HI_MPI_VI_EnableDev(ViDev);
    if(s32Ret != HI_SUCCESS)
    {
        MPP_OUTPUT("[MPP40-VI-ERROR]HI_MPI_VI_EnableDev failed with %#x!\n", s32Ret);
        vi_uninit();
        return -1;
    }
    return 0;
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：vi_bind_pipe
//|  功能描述：VI绑定PIPE
//|  参数说明：无
//|  返回值说明：成功返回0
//|  备注：Hi3516EV200有两个PIPE，每个PIPE有一个物理通道，但是只有一个VI设备。
//+------------------------------------------------------------------------------------------+
static int vi_bind_pipe(void)
{
    HI_S32 s32Ret = HI_SUCCESS;
    VI_DEV_BIND_PIPE_S stDevBindPipe = {0};
    stDevBindPipe.u32Num = 1;
    stDevBindPipe.PipeId[0] = ViPipe;
    s32Ret = HI_MPI_VI_SetDevBindPipe(ViDev, &stDevBindPipe);
    if(s32Ret != HI_SUCCESS)
    {
        MPP_OUTPUT("[MPP40-VI-ERROR]HI_MPI_VI_SetDevBindPipe failed with %#x!\n",s32Ret);
        vi_uninit();
        return -1;
    }
    return 0;
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：start_pipe
//|  功能描述：设置PIPE属性并启动PIPE
//|  参数说明：无
//|  返回值说明：成功返回0
//|  备注：
//+------------------------------------------------------------------------------------------+
static int start_pipe(void)
{
    HI_S32 s32Ret = HI_SUCCESS;
    VI_PIPE_ATTR_S stPipeAttr;
#ifdef SENSOR_IMX307
    hi_memcpy(&stPipeAttr,sizeof(VI_PIPE_ATTR_S),&PIPE_ATTR_1920x1080_RAW12_420_3DNR_RFR,sizeof(VI_PIPE_ATTR_S));
#endif
#ifdef SENSOR_GC2053
    hi_memcpy(&stPipeAttr,sizeof(VI_PIPE_ATTR_S),&PIPE_ATTR_1920x1080_RAW10_420_3DNR_RFR,sizeof(VI_PIPE_ATTR_S));
#endif
    /*
        stPipeAttr.bIspBypass = HI_TRUE;
        stPipeAttr.enPixFmt   = PIXEL_FORMAT_YVU_SEMIPLANAR_420;
        stPipeAttr.enBitWidth = DATA_BITWIDTH_8;
    */
    stPipeAttr.enPipeBypassMode = VI_PIPE_BYPASS_NONE;
    stPipeAttr.enCompressMode = COMPRESS_MODE_NONE;//[说明]Hi3516EV200仅支持非压缩
    s32Ret = HI_MPI_VI_CreatePipe(ViPipe, &stPipeAttr);
    if(s32Ret != HI_SUCCESS)
    {
        MPP_OUTPUT("[MPP40-VI-ERROR]HI_MPI_VI_CreatePipe failed with %#x!\n", s32Ret);
        vi_uninit();
        return -1;
    }
#if 1//single PIPE
    if(1)
    {
        s32Ret = HI_MPI_VI_SetPipeVCNumber(ViPipe, 0);
        if(s32Ret != HI_SUCCESS)
        {
            HI_MPI_VI_DestroyPipe(ViPipe);
            MPP_OUTPUT("[MPP40-VI-ERROR]HI_MPI_VI_SetPipeVCNumber failed with %#x!\n", s32Ret);
            vi_uninit();
            return -1;
        }
    }

    s32Ret = HI_MPI_VI_StartPipe(ViPipe);
    if(s32Ret != HI_SUCCESS)
    {
        HI_MPI_VI_DestroyPipe(ViPipe);
        MPP_OUTPUT("[MPP40-VI-ERROR]HI_MPI_VI_StartPipe failed with %#x!\n", s32Ret);
        vi_uninit();
        return -1;
    }
#endif
    return 0;
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：enable_vi_channel
//|  功能描述：使能VI通道
//|  参数说明：无
//|  返回值说明：成功返回0
//|  备注：此处使能的VI通道，是绑定的PIPE对应的物理通道。
//+------------------------------------------------------------------------------------------+
static int enable_vi_channel(void)
{
    HI_S32 s32Ret = HI_SUCCESS;
    VI_CHN_ATTR_S stChnAttr;
    hi_memcpy(&stChnAttr,
              sizeof(VI_CHN_ATTR_S),
              &CHN_ATTR_1920x1080_420_SDR8_LINEAR,
              sizeof(VI_CHN_ATTR_S));

    if(1)//VI设备线性模式：WDR_MODE_NONE
    {
        s32Ret = HI_MPI_VI_SetChnAttr(ViPipe,ViChn,&stChnAttr);
        if(s32Ret != HI_SUCCESS)
        {
            MPP_OUTPUT("[MPP40-VI-ERROR]HI_MPI_VI_SetChnAttr failed with %#x!\n", s32Ret);
            vi_uninit();
            return -1;
        }
        
        s32Ret = HI_MPI_VI_EnableChn(ViPipe, ViChn);
        if(s32Ret != HI_SUCCESS)
        {
            MPP_OUTPUT("[MPP40-VI-ERROR]HI_MPI_VI_EnableChn failed with %#x!\n", s32Ret);
            vi_uninit();
            return -1;
        }
    }
    return 0;
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：stop_isp
//|  功能描述：停止ISP
//|  参数说明：无
//|  返回值说明：无
//|  备注：
//+------------------------------------------------------------------------------------------+
static void stop_isp(void)
{
    if(g_IspPid)
    {
        HI_MPI_ISP_Exit(ViPipe);
        pthread_join(g_IspPid, NULL);
        
        ALG_LIB_S stAwbLib;
        stAwbLib.s32Id = ViPipe;
        strncpy(stAwbLib.acLibName, HI_AWB_LIB_NAME, sizeof(HI_AWB_LIB_NAME));
        HI_MPI_AWB_UnRegister(ViPipe, &stAwbLib);
        
        ALG_LIB_S stAeLib;
        stAeLib.s32Id = ViPipe;
        strncpy(stAeLib.acLibName, HI_AE_LIB_NAME, sizeof(HI_AE_LIB_NAME));
        HI_MPI_AE_UnRegister(ViPipe, &stAeLib);
        
        ISP_Sensor_UnRegiter_callback(ViPipe);
        
        g_IspPid=0;
    }
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：run_isp
//|  功能描述：配置并启动ISP
//|  参数说明：无
//|  返回值说明：成功返回0
//|  备注：
//+------------------------------------------------------------------------------------------+
static int run_isp(void)
{
    HI_S32 s32Ret = HI_SUCCESS;
    HI_U32 u32SnsId = 0;
    ISP_PUB_ATTR_S stPubAttr;
    
    //----------------------------------------
    //[说明]更新ISP控制信息
    //此处的设置只作为调试时使用，根据帧率设置
    //了ISP信息刷新时间间隔。由于ISP和Pipe相关，
    //所以该参数设置也需要指定Pipe。
    //----------------------------------------
#if 0
    HI_U32 u32FrameRate = 30;//sensor的帧率
    ISP_CTRL_PARAM_S stIspCtrlParam;//ISP控制参数结构体
    s32Ret = HI_MPI_ISP_GetCtrlParam(ViPipe, &stIspCtrlParam);
    if(HI_SUCCESS != s32Ret)
    {
        MPP_OUTPUT("[MPP40-VI-ERROR]HI_MPI_ISP_GetCtrlParam failed with %d!\n",s32Ret);
        return -1;
    }
    stIspCtrlParam.u32StatIntvl = u32FrameRate/30;
    if(stIspCtrlParam.u32StatIntvl == 0)
    {
        stIspCtrlParam.u32StatIntvl = 1;
    }
    s32Ret = HI_MPI_ISP_SetCtrlParam(ViPipe, &stIspCtrlParam);
    if(HI_SUCCESS != s32Ret)
    {
        MPP_OUTPUT("[MPP40-VI-ERROR]HI_MPI_ISP_SetCtrlParam failed with %d!\n",s32Ret);
        return -1;
    }
#endif

    //----------------------------------------
    //[说明]若不使用海思的ISP，需运行自己的
    //ISP程序。
    //----------------------------------------
#if 1
    memcpy(&stPubAttr, &ISP_PUB_ATTR_MIPI_2M_30FPS, sizeof(ISP_PUB_ATTR_S));
    //stPubAttr.enWDRMode=WDR_MODE_NONE;

    if((WDR_MODE_NONE == stPubAttr.enWDRMode)&&(ViPipe == 0))//线性模式需要PIPE，仅PIPE0支持
    {
        s32Ret = ISP_Sensor_Regiter_callback(ViPipe, u32SnsId);
        if(HI_SUCCESS != s32Ret)
        {
            MPP_OUTPUT("[MPP40-VI-ERROR]Register sensor %d to ISP %d failed!\n", u32SnsId, ViPipe);
            stop_isp();
            vi_uninit();
            return -1;
        }

        s32Ret = ISP_BindSns(ViPipe, u32SnsId, 0);
        if(HI_SUCCESS != s32Ret)
        {
            MPP_OUTPUT("[MPP40-VI-ERROR]Register sensor %d bus id %d failed!\n", u32SnsId, 0);
            stop_isp();
            vi_uninit();
            return -1;
        }
        
        ALG_LIB_S stAeLib;
        stAeLib.s32Id = ViPipe;
        strncpy(stAeLib.acLibName, HI_AE_LIB_NAME, sizeof(HI_AE_LIB_NAME));
        s32Ret = HI_MPI_AE_Register(ViPipe, &stAeLib);
        if(HI_SUCCESS != s32Ret)
        {
            MPP_OUTPUT("[MPP40-VI-ERROR]HI_MPI_AE_Register failed!\n");
            stop_isp();
            vi_uninit();
            return -1;
        }

        ALG_LIB_S stAwbLib;
        stAwbLib.s32Id = ViPipe;
        strncpy(stAwbLib.acLibName, HI_AWB_LIB_NAME, sizeof(HI_AWB_LIB_NAME));
        s32Ret = HI_MPI_AWB_Register(ViPipe, &stAwbLib);
        if(HI_SUCCESS != s32Ret)
        {
            MPP_OUTPUT("[MPP40-VI-ERROR]HI_MPI_AWB_Register failed!\n");
            stop_isp();
            vi_uninit();
            return -1;
        }

        s32Ret = HI_MPI_ISP_MemInit(ViPipe);
        if(s32Ret != HI_SUCCESS)
        {
            MPP_OUTPUT("[MPP40-VI-ERROR]Init Ext memory failed with %#x!\n", s32Ret);
            stop_isp();
            vi_uninit();
            return -1;
        }

        s32Ret = HI_MPI_ISP_SetPubAttr(ViPipe, &stPubAttr);
        if(s32Ret != HI_SUCCESS)
        {
            MPP_OUTPUT("[MPP40-VI-ERROR]SetPubAttr failed with %#x!\n", s32Ret);
            stop_isp();
            vi_uninit();
            return -1;
        }

        s32Ret = HI_MPI_ISP_Init(ViPipe);
        if(s32Ret != HI_SUCCESS)
        {
            MPP_OUTPUT("[MPP40-VI-ERROR]ISP Init failed with %#x!\n", s32Ret);
            stop_isp();
            vi_uninit();
            return -1;
        }
        
        /*自动/手动曝光设置*/
        if(pstVI_para->enAUTOExposure == HI_FALSE)
        {
            ISP_EXPOSURE_ATTR_S ispExposureAttr;
            s32Ret = HI_MPI_ISP_GetExposureAttr(ViPipe, &ispExposureAttr);
            if(s32Ret != HI_SUCCESS)
            {
                MPP_OUTPUT("[MPP40-VI-ERROR]ISP get exposure attr failed with %#x!",s32Ret);
                MPP_OUTPUT("The program will ignore this setting.\n");
                goto IGNORE_AE_ATTR_SET;
            }
            ispExposureAttr.bByPass                   = HI_FALSE;
            ispExposureAttr.enOpType                  = OP_TYPE_MANUAL;
            ispExposureAttr.u8AERunInterval           = 1;//AE算法运行间隔时间，取值1~255，默认为1
            ispExposureAttr.stManual.enAGainOpType    = OP_TYPE_MANUAL;
            ispExposureAttr.stManual.enDGainOpType    = OP_TYPE_MANUAL;
            ispExposureAttr.stManual.enISPDGainOpType = OP_TYPE_MANUAL;
            ispExposureAttr.stManual.enExpTimeOpType  = OP_TYPE_MANUAL;
            ispExposureAttr.stManual.u32AGain         = 0x400;
            ispExposureAttr.stManual.u32DGain         = 0x400;
            ispExposureAttr.stManual.u32ISPDGain      = 0x400;
            ispExposureAttr.stManual.u32ExpTime       = pstVI_para->exposuretime;
            s32Ret = HI_MPI_ISP_SetExposureAttr(ViPipe, &ispExposureAttr);
            if(s32Ret != HI_SUCCESS)
            {
                MPP_OUTPUT("[MPP40-VI-ERROR]ISP set exposure attr failed with %#x!\n", s32Ret);
            }
            else
            {
                MPP_OUTPUT("[MPP40-VI-INFO]Set Exposure to manual successful!\n");
            }
        }
        else
        {
        #if 0 //[自注]手册上的示例，有错误
            ISP_EXPOSURE_ATTR_S stExpAttr;
            HI_MPI_ISP_GetExposureAttr(ViPipe, &stExpAttr);
            stExpAttr.bByPass                      = HI_FALSE;
            stExpAttr.enOpType                     = OP_TYPE_AUTO;
            stExpAttr.stAuto.stExpTimeRange.u32Max = 40000;
            stExpAttr.stAuto.stExpTimeRange.u32Min = 10;
            HI_MPI_ISP_SetExposureAttr(ViPipe, &stExpAttr);
            
            stExpAttr.stAuto.u8Speed = 0x80;
            HI_MPI_ISP_SetExposureAttr(ViPipe, &stExpAttr);
            
            stExpAttr.stAuto.enAEStrategyMode  = AE_EXP_HIGHLIGHT_PRIOR;
            stExpAttr.stAuto.u16HistRatioSlope = 0x100;
            stExpAttr.stAuto.u8MaxHistOffset   = 0x40;
            HI_MPI_ISP_SetExposureAttr(ViPipe, &stExpAttr);
            
            stExpAttr.stAuto.stAntiflicker.bEnable     = HI_TRUE;
            stExpAttr.stAuto.stAntiflicker.u8Frequency = 50;
            stExpAttr.stAuto.stAntiflicker.enMode      = ISP_ANTIFLICKER_NORMAL_MODE;
            HI_MPI_ISP_SetExposureAttr(ViPipe, &stExpAttr);
            
            stExpAttr.stAuto.stAEDelayAttr.u16BlackDelayFrame = 10;
            stExpAttr.stAuto.stAEDelayAttr.u16WhiteDelayFrame = 0;
            HI_MPI_ISP_SetExposureAttr(ViPipe, &stExpAttr);
            
            HI_U8 i,j;
            HI_U8 u8Weighttable[15][17] = 
            {
                {1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0},
                {1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0},
                {1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0},
                {1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0},
                {1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0},
                {1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0},
                {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
                {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
                {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
                {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
                {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
                {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
                {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
                {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
                {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
            };
            for (i = 0; i < AE_ZONE_ROW; i++)
            {
                for (j = 0; j < AE_ZONE_COLUMN; j++)
                { 
                    stExpAttr.stAuto.au8Weight[i][j] = u8Weighttable[i][j];
                }
            }
            HI_MPI_ISP_SetExposureAttr(ViPipe, &stExpAttr);
        #endif
        }
IGNORE_AE_ATTR_SET:

        s32Ret = ISP_Run(ViPipe);
        if(s32Ret != HI_SUCCESS)
        {
            MPP_OUTPUT("[MPP40-VI-ERROR]ISP Run failed with %#x!\n", s32Ret);
            stop_isp();
            vi_uninit();
            return -1;
        }
    }
#endif
    return 0;
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：vi_uninit
//|  功能描述：VI去初始化
//|  参数说明：无
//|  返回值说明：无
//|  备注：
//+------------------------------------------------------------------------------------------+
void vi_uninit(void)
{
    HI_MPI_VI_DisableChn(ViPipe, ViChn);
    
    stop_isp();
    
    //----------------------------------------
    //[说明]函数本身具有返回值，但去初始化发生
    //在退出时一般不关心返回值。
    //----------------------------------------
    HI_MPI_VI_StopPipe(ViPipe);
    HI_MPI_VI_DestroyPipe(ViPipe);
    HI_MPI_VI_DisableDev(ViDev);
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：vi_init
//|  功能描述：VI初始化
//|  参数说明：自定义的VI初始化参数结构体指针
//|  返回值说明：成功返回0
//|  备注：
//+------------------------------------------------------------------------------------------+
int vi_init(VI_PARA_S *p)
{
    if(p != NULL)
    {
        pstVI_para = p;
    }
    
    do
    {
        /* 1.设置并启动MIPI */
        if(set_mipi() != 0)break;
        
        /* 2.设置VI/VPSS离在线模式 */
        //----------------------------------------
        // 2021-6-4
        // 当使用VPSS时，可在此处设置VI-VPSS模式
        //----------------------------------------
        
        /* 3.使能VI设备 */
        if(enable_vi_device() != 0)break;
        
        /* 4.绑定VI设备和PIPE */
        if(vi_bind_pipe() != 0)break;
        
        /* 5.启动VI PIPE */
        if(start_pipe() != 0)break;
        
        /* 6.启动VI通道(Chn) */
        if(enable_vi_channel() != 0)break;
        
        /* 7.创建ISP */
        if(run_isp() != 0)break;
        
        return 0;
    }while(0);
    return -1;
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：ISP_Sensor_Regiter_callback
//|  功能描述：ISP中sensor注册函数。
//|  参数说明：略
//|  返回值说明：成功返回HI_SUCCESS
//|  备注：从sample中复制过来、简单裁剪的。
//+------------------------------------------------------------------------------------------+
static HI_S32 ISP_Sensor_Regiter_callback(ISP_DEV IspDev, HI_U32 u32SnsId)
{
    ALG_LIB_S stAeLib;
    ALG_LIB_S stAwbLib;
    const ISP_SNS_OBJ_S *pstSnsObj;
    HI_S32 s32Ret = -1;

#ifdef SENSOR_IMX307
    pstSnsObj = &stSnsImx307_2l_Obj;
#endif
#ifdef SENSOR_GC2053
    pstSnsObj = &stSnsGc2053Obj;
#endif
    if(HI_NULL == pstSnsObj)
    {
        MPP_OUTPUT("[MPP40-VI-ERROR]sensor[%d] not exist!\n", u32SnsId);
        return HI_FAILURE;
    }

    stAeLib.s32Id = IspDev;
    stAwbLib.s32Id = IspDev;
    strncpy(stAeLib.acLibName, HI_AE_LIB_NAME, sizeof(HI_AE_LIB_NAME));
    strncpy(stAwbLib.acLibName, HI_AWB_LIB_NAME, sizeof(HI_AWB_LIB_NAME));
    //  strncpy(stAfLib.acLibName,HI_AF_LIB_NAME,sizeof(HI_AF_LIB_NAME));

    if(pstSnsObj->pfnRegisterCallback != HI_NULL)
    {
        s32Ret = pstSnsObj->pfnRegisterCallback(IspDev, &stAeLib, &stAwbLib);
        if(s32Ret != HI_SUCCESS)
        {
            MPP_OUTPUT("[MPP40-VI-ERROR]sensor_register_callback failed with %#x!\n", s32Ret);
            return s32Ret;
        }
    }
    else
    {
        MPP_OUTPUT("[MPP40-VI-ERROR]sensor_register_callback failed with HI_NULL!\n");
    }

    return HI_SUCCESS;
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：ISP_Sensor_UnRegiter_callback
//|  功能描述：ISP中sensor反注册函数。
//|  参数说明：略
//|  返回值说明：成功返回HI_SUCCESS
//|  备注：从sample中复制过来、简单裁剪的。
//+------------------------------------------------------------------------------------------+
static HI_S32 ISP_Sensor_UnRegiter_callback(ISP_DEV IspDev)
{
    ALG_LIB_S stAeLib;
    ALG_LIB_S stAwbLib;
    const ISP_SNS_OBJ_S *pstSnsObj;
    HI_S32 s32Ret = -1;

#ifdef SENSOR_IMX307
    pstSnsObj = &stSnsImx307_2l_Obj;
#endif
#ifdef SENSOR_GC2053
    pstSnsObj = &stSnsGc2053Obj;
#endif
    if(HI_NULL == pstSnsObj)
    {
        return HI_FAILURE;
    }

    stAeLib.s32Id = IspDev;
    stAwbLib.s32Id = IspDev;
    strncpy(stAeLib.acLibName, HI_AE_LIB_NAME, sizeof(HI_AE_LIB_NAME));
    strncpy(stAwbLib.acLibName, HI_AWB_LIB_NAME, sizeof(HI_AWB_LIB_NAME));
    //strncpy(stAfLib.acLibName,HI_AF_LIB_NAME,sizeof(HI_AF_LIB_NAME));

    if(pstSnsObj->pfnUnRegisterCallback != HI_NULL)
    {
        s32Ret = pstSnsObj->pfnUnRegisterCallback(IspDev, &stAeLib, &stAwbLib);

        if(s32Ret != HI_SUCCESS)
        {
            MPP_OUTPUT("[MPP40-VI-ERROR]sensor_unregister_callback failed with %#x!\n", s32Ret);
            return s32Ret;
        }
    }
    else
    {
        MPP_OUTPUT("[MPP40-VI-ERROR]sensor_unregister_callback failed with HI_NULL!\n");
    }

    return HI_SUCCESS;
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：ISP_BindSns
//|  功能描述：ISP和sensor绑定函数。
//|  参数说明：略
//|  返回值说明：成功返回HI_SUCCESS
//|  备注：从sample中复制过来、简单裁剪的。
//+------------------------------------------------------------------------------------------+
static HI_S32 ISP_BindSns(ISP_DEV IspDev, HI_U32 u32SnsId, HI_S8 s8SnsDev)
{
    ISP_SNS_COMMBUS_U    uSnsBusInfo;
    ISP_SNS_TYPE_E       enBusType;
    const ISP_SNS_OBJ_S *pstSnsObj;
    HI_S32               s32Ret;

#ifdef SENSOR_IMX307
    pstSnsObj = &stSnsImx307_2l_Obj;
#endif
#ifdef SENSOR_GC2053
    pstSnsObj = &stSnsGc2053Obj;
#endif
    if(HI_NULL == pstSnsObj)
    {
        MPP_OUTPUT("[MPP40-VI-ERROR]sensor %d not exist!\n", u32SnsId);
        return HI_FAILURE;
    }

    enBusType = ISP_SNS_I2C_TYPE;//仅I2C
    if(ISP_SNS_I2C_TYPE == enBusType)
    {
        uSnsBusInfo.s8I2cDev = s8SnsDev;
    }
    else
    {
        uSnsBusInfo.s8SspDev.bit4SspDev = s8SnsDev;
        uSnsBusInfo.s8SspDev.bit4SspCs = 0;
    }

    s32Ret = pstSnsObj->pfnSetBusInfo(IspDev, uSnsBusInfo);
    if(s32Ret != HI_SUCCESS)
    {
        MPP_OUTPUT("[MPP40-VI-ERROR]set sensor bus info failed with %#x!\n", s32Ret);
        return s32Ret;
    }

    return s32Ret;
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：ISP_Thread
//|  功能描述：ISP线程。HI_MPI_ISP_Run是阻塞函数。
//|  参数说明：略
//|  返回值说明：无
//|  备注：从sample中复制过来、简单裁剪的。
//+------------------------------------------------------------------------------------------+
static void* ISP_Thread(void *p)
{
    HI_S32 s32Ret;
    ISP_DEV IspDev;
    HI_CHAR szThreadName[20];

    IspDev = (ISP_DEV)p;
    snprintf(szThreadName, 20, "ISP%d_RUN", IspDev);
    prctl(PR_SET_NAME, szThreadName, 0, 0, 0);

    s32Ret = HI_MPI_ISP_Run(IspDev);
    if(HI_SUCCESS != s32Ret)
    {
        MPP_OUTPUT("[MPP40-VI-ERROR]HI_MPI_ISP_Run failed with %#x!\n", s32Ret);
    }

    return NULL;
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：ISP_Run
//|  功能描述：创建ISP处理线程。
//|  参数说明：略
//|  返回值说明：成功返回0
//|  备注：从sample中复制过来、简单裁剪的。
//+------------------------------------------------------------------------------------------+
static HI_S32 ISP_Run(ISP_DEV IspDev)
{
    HI_S32 s32Ret = 0;
    pthread_attr_t *pstAttr = NULL;

    s32Ret = pthread_create(&g_IspPid, pstAttr, ISP_Thread, (HI_VOID *)IspDev);

    if(0 != s32Ret)
    {
        MPP_OUTPUT("[MPP40-VI-ERROR]Create isp running thread failed!,error: %d,%s!\r\n", s32Ret, strerror(s32Ret));
        goto out;
    }

out:
    if(NULL != pstAttr)
    {
        pthread_attr_destroy(pstAttr);
    }

    return s32Ret;
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：get_isp_exposure_time
//|  功能描述：获取ISP当前曝光时间
//|  参数说明：无
//|  返回值说明：返回查询的当前曝光时间
//|  备注：
//+------------------------------------------------------------------------------------------+
uint32_t get_isp_exposure_time(void)
{
    ISP_EXP_INFO_S stExpInfo;
    HI_MPI_ISP_QueryExposureInfo(ViPipe, &stExpInfo);
    return stExpInfo.u32ExpTime;
}